#!/usr/bin/python3
#******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: DisNight
# Create: 2020-12-28
# ******************************************************************************/

"""
need yum >= 4.2.15-9
need libabigail
"""

from src.util.file_utils import load_simple_file, save_dict_file, save_simple_file, load_dict_file
from src.util.check_abi import CheckAbi
from src.util.dnf_utils import find_update_install_rpms, get_all_repolist
from src.download_rpms import get_bin_rpmlist, download_required_rpm, download_all_pkgbin
from src.find_whatrequires import get_all_berequired
from src.change_bin2src import change_all_bin2src
from src.repo_version import compare_pkg_ver, DNFPkgVer, OBSPkgVer

import argparse
import os
import logging

logging.getLogger().setLevel(logging.INFO)

def find_all_effect_bin_rpms(save_dir, required_dir, src2bin_map, work_dir="/var/tmp", dirs=["old", "new"]):
    if not dirs or not isinstance(dirs, list):
        logging.error("illegal dirs type: %s", str(type(dirs)))
        return set()
    effect_bin_rpms = set()
    for pkg_name in src2bin_map:
        pre_dir = os.path.join(save_dir, pkg_name, dirs[0])
        ori_dir = os.path.join(save_dir, pkg_name, dirs[-1])
        paths = [pre_dir, ori_dir]
        check_abi_process = CheckAbi(work_dir, "", False, False, required_dir)
        check_abi_process.process_with_rpms(paths)
        logging.info("pkg: %s find %d effect rpms", pkg_name, len(check_abi_process.effect_rpm_list))
        effect_bin_rpms |= check_abi_process.effect_rpm_list
    return effect_bin_rpms


def find_all_update_install_rpms(save_dir, src2bin, repos):
    update_bin_set = set()
    for pkg_name in src2bin:
        work_dir = os.path.join(save_dir, pkg_name, "new")
        if not os.path.isdir(work_dir):
            logging.warning("illegal path for pkg: %s", pkg_name)
            continue
        update_bin_set = update_bin_set | find_update_install_rpms(pkg_name, work_dir, repos)
    return update_bin_set


def find_all_updated(args, init_list, save_file=True):
    new_update_list = init_list
    all_pkg2bin = {}
    index = 1
    while new_update_list:
        print("update_list: " + str(new_update_list))
        logging.info("find all update rpms round %d", index)
        index += 1
        now_list = new_update_list
        new_update_list = []
        # download bin rpms in obs and repo
        pkg2bin_map = get_bin_rpmlist(now_list, obs_prj="openEuler:20.03:LTS:SP1")
        download_all_pkgbin(args.savedir, pkg2bin_map, args.old_repos, args.new_repos)
        # download installed 
        now_required_rpms = get_all_berequired(args.savedir, pkg2bin_map, repos=args.old_repos)
        save_simple_file(args.savedir, "required_list_{}".format(index - 1), now_required_rpms)
        download_required_rpm(args.downloaddir, now_required_rpms, args.old_repos)
        # find updated install requires
        all_pkg2bin.update(pkg2bin_map)
        if save_file:
            save_dict_file(args.savedir, "pkg2bin.json", all_pkg2bin)
        update_install_set = find_all_update_install_rpms(args.savedir, pkg2bin_map, args.new_repos)
        # update all src2bin map, find  updated installed pkg
        update_effect_rpms = find_all_effect_bin_rpms(args.savedir, args.downloaddir, pkg2bin_map)
        update_src_set = change_all_bin2src(update_install_set, all_pkg2bin) | change_all_bin2src(update_effect_rpms, all_pkg2bin)
        new_update_list = list(update_src_set)
    logging.info("all finished")
    return all_pkg2bin


def check_input_repo(args):
    if not os.path.exists(args.repoconf):
        logging.error("path of repo conf file is not exist")
        return False
    exist_repos = get_all_repolist(repo_file=args.repoconf)
    if not check_repo(args.old_repos, exist_repos, "old"):
        return False
    if not check_repo(args.new_repos, exist_repos, "new"):
        return False
    return True

def check_repo(input_repos, exist_repos, type_title):
    unexist_repos = set()
    for repo in input_repos:
        if not repo in exist_repos:
            unexist_repos.add(repo)
    if not unexist_repos:
        logging.debug("check %s repos pass", type_title)
        return True
    logging.error("%s repo list: %s not exist", type_title, ", ".join(list(unexist_repos)))
    return False


def check_args(args):
    if not args.cvelist and not os.path.exists(args.cvelist):
        logging.error("path of cve pkgs is not exists")
        return False
    if not check_input_repo(args):
        logging.error("input repo is illegal")
        return False
    return True

def check_package_version(pkg_list, args):
    changed_list = []
    unchanged_list = []
    for pkg in pkg_list:
        if compare_pkg_ver(pkg, ["everything-src", "update-src"], args.branch.replace("-", ":"), origin_repo=DNFPkgVer, new_repo=OBSPkgVer):
            changed_list.append(pkg)
        else:
            unchanged_list.append(pkg)
    return changed_list, unchanged_list

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-l", "--cvelist", default="",
                        help="path of cve pkgs")
    # input of new repo name
    parser.add_argument("-b", "--branch", type=str, required=True,
                        help="openEuler branch .e.s openEuler-20.03-LTS-SP1")
    parser.add_argument("-o", "--old_repos", type=str, nargs='*', required=True,
                        help="repo list of old repo")
    parser.add_argument("-n", "--new_repos", type=str, nargs='*', required=True,
                        help="repo list of new repo")

    parser.add_argument("-s", "--savedir", default="/tmp/update-package/pkg_rpm",
                        help="dir of download dir")
    parser.add_argument("-d", "--downloaddir", default="/tmp/update-package/download",
                        help="dir of require download dir")
    parser.add_argument("--repoocnf", default="",
                        help="conf file of yum.repo")
    parser.add_argument("--skip_cve_list", action="store_true", default=False,
                        help="skip download pkg in cve list")
    parser.add_argument("--skip_vr_check", action="store_true", default=False,
                        help="skip compare pkg's [ver-release] in repo and in obs")
    args = parser.parse_args()
    
    if not check_args(args):
        quit()
    if not args.skip_cve_list:
        pkg_list = load_simple_file(args.cvelist)
    if not pkg_list:
        logging.warning("no package need update")
        quit()
    changed_list, unchanged_list = check_package_version(pkg_list, args)
    find_all_updated(args, pkg_list)

